package com.lanzuo.pana.adapter.client.watcher;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.jcraft.jsch.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.lanzuo.pana.adapter.constant.Constants.ARGUMENT_FIND_COMMAND;

/**
 * @author <a href="eric_zheng@lanzuo.com.cn">eric</a>
 * @date 2025/06/25 15:56
 * @description
 */
@Slf4j
public class LanzuoChannelSftp {

    ChannelSftp channelSftp;

    Session session;

    @Getter
    SshConfig config;

    public LanzuoChannelSftp(Session session, ChannelSftp channelSftp, SshConfig config) {
        this.session = session;
        this.channelSftp = channelSftp;
        this.config = config;
    }

    /**
     * 递归列出远程目录下所有文件和子目录
     */
    public List<SftpFile> lsRecursively(String dirPath) throws SftpException {
        Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(dirPath);

        if (entries == null || entries.isEmpty()) {
            return new ArrayList<>();
        }

        List<SftpFile> result = new ArrayList<>();
        for (ChannelSftp.LsEntry entry : entries) {
            String filename = entry.getFilename();
            if (".".equals(filename) || "..".equals(filename)) {
                // 忽略 . 和 ..
                continue;
            }

            boolean isDirectory = entry.getAttrs().isDir();

            String filePath = dirPath + "/" + filename;
            // 秒级时间戳
            long lastModifyTime = entry.getAttrs().getMTime();
            long size = entry.getAttrs().getSize();
            int permissions = entry.getAttrs().getPermissions();
            // 如果是目录，递归进入子目录
            if (isDirectory) {
                result.addAll(lsRecursively(filePath));
            } else {
                // 格式化时间
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String formattedTime = sdf.format(new Date(lastModifyTime * 1000L));

                SftpFile sftpFile = new SftpFile();
                sftpFile.setFileName(filename);
                sftpFile.setFilePath(filePath);
                sftpFile.setFileSize(size);
                sftpFile.setPermissions(String.format("%o", permissions));
                sftpFile.setLastModifyTime(formattedTime);
                result.add(sftpFile);
            }
        }

        return result;
    }

    /**
     * 根据文件路径获取文件内容
     *
     * @param filePath 文件路径
     * @return 文件内容
     */
    public Pair<String, String> getFile(String filePath) {
        try (InputStream inputStream = channelSftp.get(filePath)) {
            List<String> fileContent = IoUtil.readLines(inputStream, Charset.defaultCharset(), new ArrayList<>());
            return Pair.of(filePath, CollUtil.join(fileContent, StrUtil.CRLF));
        } catch (Exception e) {
            log.error("无法读取文件内容：{}", e.getMessage());
            return Pair.of(filePath, "Unknown content");
        }
    }

    /**
     * 通过find 指令来查询所有文件信息
     * 后通过文件流获取文件内容
     *
     * @param dirPath 文件路径
     * @param logType 需要获取的文件类型
     * @param checker 文件校验逻辑
     * @return 返回文件名称, 路径, 大小, 最后一次修改时间
     */
    public Deque<SftpFile> find(String dirPath, String logType, String extraArgs, FileChecker checker) {
        ChannelExec channelExec = null;

        Deque<SftpFile> result = new ArrayDeque<>();
        try {
            channelExec = (ChannelExec) session.openChannel("exec");
            String command = StrUtil.format(ARGUMENT_FIND_COMMAND, dirPath, StrUtil.isBlank(logType) ? "*" : logType, extraArgs);
            channelExec.setCommand(command);
            InputStream in = channelExec.getInputStream();
            channelExec.connect();

            ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int read;

            while ((read = in.read(buffer)) > 0) {
                responseStream.write(buffer, 0, read);
            }

            channelExec.disconnect();

            String output = responseStream.toString();
            String[] lines = output.split("\n");

            // 第二步：对每个文件读取内容并封装对象
            for (String line : lines) {
                if (line.trim().isEmpty()) continue;

                String[] parts = line.split("\t");
                String filePath = parts[0];
                String filename = new File(filePath).getName();

                // 存在需要忽略的文件, 跳过
                if (ObjectUtil.isNotNull(checker) && checker.existCheck(filename)) {
                    continue;
                }

                long fileSize = Long.parseLong(parts[1]);
                String lastModifyTime = parts[2].split("\\.")[0];

                SftpFile sftpFile = new SftpFile();
                sftpFile.setFileName(filename);
                sftpFile.setFilePath(filePath);
                sftpFile.setFileSize(fileSize);
                sftpFile.setLastModifyTime(lastModifyTime);

                result.add(sftpFile);
            }
        } catch (JSchException | IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (channelExec != null) channelExec.disconnect();
        }

        return result;
    }

}
